use yaml_rust::YamlLoader;

use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::string::String;

use regex::Regex;

struct SwitchModifyNode {
    file: String,
    encoding: String,
    pattern: String,
    replace: String,
}

/**
 * 描述一个profile配置
 * 
 * @author liuyusong
 */
struct SwitchProfile {
    id: String,
    description: Option<String>,
    switch_list: Vec<SwitchModifyNode>
}

struct SwitchParser {
    id: Option<String>,
    description: Option<String>,
    param_map: HashMap<String, String>,
    profile_map: HashMap<String, SwitchProfile>
}

impl SwitchParser {
    pub fn new() -> SwitchParser {
        SwitchParser {
            id: Option::None,
            description: Option::None,
            param_map: HashMap::new(),
            profile_map: HashMap::new()
        }
    }
    
    /**
     * 从yaml配置文件中加载配置
     */
    pub fn load_from_yaml(&mut self, path: &str) -> bool {
        let result = fs::read_to_string(path);
        if result.is_err() {
            return false;
        }
        
        let obj = YamlLoader::load_from_str(result.unwrap().as_str());
        if obj.is_err() {
            return false;
        }
        
        let obj = &obj.unwrap()[0];
        
        self.id = Some(String::from(obj["id"].as_str().unwrap()));
        self.description = Some(String::from(obj["description"].as_str().unwrap()));
        
        // 解析switchList
        let switch_list = &obj["switchList"];
        if switch_list.is_badvalue() {
            return false
        }
        
        if !switch_list.is_array() {
            return false;
        }
        
        let switch_list = switch_list.as_vec().unwrap();
        for one in switch_list.iter() {
            let id = one["id"].clone().into_string().unwrap();
            let desc = one["description"].clone().into_string();
            let config_list = one["configList"].as_vec().unwrap();
            
            let mut profile = SwitchProfile {
                id: id.clone(),
                description: desc,
                switch_list: Vec::new()
            };
            
            for conf in config_list.iter() {
                let file = conf["file"].as_str();
                let encoding = conf["encoding"].as_str();
                let modify_list = conf["modifyList"].as_vec().unwrap();
                
                let switch_list = &mut profile.switch_list;
                
                for modify in modify_list.iter() {
                    let node = SwitchModifyNode {
                        file: file.clone().unwrap().to_string(),
                        encoding: encoding.clone().unwrap().to_string(),
                        pattern: modify["pattern"].clone().into_string().unwrap(),
                        replace: modify["replace"].clone().into_string().unwrap()
                    };
                    
                    switch_list.push(node);
                }
            }
            
            self.profile_map.insert(id.clone(), profile);
        }
        
        true
    }
    
    /**
     * 枚举所有的profile
     */
    pub fn enum_profile(&self) -> Vec<(String, Option<String>)> {
        let mut profile_list = Vec::new();
        
        for profile in self.profile_map.values() {
            profile_list.push((profile.id.clone(), profile.description.clone()));
        }
        
        profile_list
    }

    /**
     * 切换配置到指定的profile
     * 
     */
    pub fn switch_to(&self, profile_id: &str, is_dry_run: bool) -> bool {
        if !self.profile_map.contains_key(profile_id) {
            println!("cannot find profile_id {}", profile_id);
            return false;
        }
        
        let profile = self.profile_map.get(profile_id).unwrap();
        
        for modify in profile.switch_list.iter() {
            let file_path = Path::new(modify.file.as_str());
            if !file_path.exists() {
                println!("cannot find file {}, skip it", modify.file);
                continue;
            }
            
            let mut file_str = fs::read_to_string(file_path).unwrap();
            
            let re = Regex::new(modify.pattern.as_str());
            if re.is_err() {
                println!("invalid regex {}", modify.pattern);
                continue;
            }
            
            let re = re.unwrap();
            
            let mut prev_end = 0;
            
            let mut start;
            let mut end;
            loop {
                {
                    let matches = re.find_at(file_str.as_mut_str(), prev_end);
                    if let Some(one) = matches {
                        start = one.start();
                        end = one.end();
                        
                        println!("\treplacing text from [{}] to [{}]",
                            one.as_str(), modify.replace);
                    } else {
                        break;
                    }
                }
                
                prev_end = start + modify.replace.len();
                if !is_dry_run {
                    file_str.replace_range(start..end, modify.replace.as_str());
                }
            }
        }
        
        true
    }
}
